---
interface Props {
  videoId: string
  title?: string
  backgroundImage?: string
  withShadowImage?: boolean
}

const { videoId, title, backgroundImage, withShadowImage } = Astro.props
---

<div class="relative flex items-center justify-center">
  {
    withShadowImage && backgroundImage && (
      <img
        src={backgroundImage}
        alt=""
        aria-hidden="true"
        class="absolute inset-0 -z-10 mx-auto scale-105 object-center opacity-70 blur-xl brightness-150"
        loading="lazy"
        decoding="async"
      />
    )
  }
  <lite-youtube
    class="rounded-lg"
    videoid={videoId}
    style={`background-image: url('${backgroundImage}')`}
    tabindex="0"
    aria-label="button"
  >
    <a
      href={`https://youtube.com/watch?v=${videoId}`}
      class="lty-playbtn"
      title={title}
      role="button"
      tabindex="0"
    >
      <span class="lyt-visually-hidden">{title}</span>
    </a>
  </lite-youtube>

  <script>
    // @ts-nocheck

    /*
	Component extracted from: https://github.com/paulirish/lite-youtube-embed/tree/master
	*/

    /**
     * A lightweight youtube embed. Still should feel the same to the user, just MUCH faster to initialize and paint.
     *
     * Thx to these as the inspiration
     *   https://storage.googleapis.com/amp-vs-non-amp/youtube-lazy.html
     *   https://autoplay-youtube-player.glitch.me/
     *
     * Once built it, I also found these:
     *   https://github.com/ampproject/amphtml/blob/master/extensions/amp-youtube (👍👍)
     *   https://github.com/Daugilas/lazyYT
     *   https://github.com/vb/lazyframe
     */
    class LiteYTEmbed extends HTMLElement {
      connectedCallback() {
        const video = this.getAttribute('videoid').split('?')
        this.videoId = video[0]
        this.short = video[1]

        let playBtnEl = this.querySelector('.lty-playbtn')
        // A label for the button takes priority over a [playlabel] attribute on the custom-element
        this.playLabel =
          (playBtnEl && playBtnEl.textContent.trim()) ||
          this.getAttribute('playlabel') ||
          'Reproducir presentación de La Velada del Año'

        this.dataset.title = this.getAttribute('title') || ''

        /**
         * Lo, the youtube poster image!  (aka the thumbnail, image placeholder, etc)
         *
         * See https://github.com/paulirish/lite-youtube-embed/blob/master/youtube-thumbnail-urls.md
         */
        if (!this.style.backgroundImage) {
          this.style.backgroundImage = `url("https://i.ytimg.com/vi/${this.videoId}/hqdefault.jpg")`
          this.upgradePosterImage()
        }

        // Set up play button, and its visually hidden label
        if (!playBtnEl) {
          playBtnEl = document.createElement('button')
          playBtnEl.type = 'button'
          playBtnEl.classList.add('lty-playbtn')
          this.append(playBtnEl)
        }
        if (!playBtnEl.textContent) {
          const playBtnLabelEl = document.createElement('span')
          playBtnLabelEl.className = 'lyt-visually-hidden'
          playBtnLabelEl.textContent = this.playLabel
          playBtnEl.append(playBtnLabelEl)
        }

        this.addNoscriptIframe()

        // On hover (or tap), warm up the TCP connections we're (likely) about to use.
        this.addEventListener('pointerover', LiteYTEmbed.warmConnections, { once: true })

        // Once the user clicks, add the real iframe and drop our play button
        // TODO: In the future we could be like amp-youtube and silently swap in the iframe during idle time
        //   We'd want to only do this for in-viewport or near-viewport ones: https://github.com/ampproject/amphtml/pull/5003
        this.addEventListener('click', this.activate)

        // For accessibility, handle keypresses as well
        this.addEventListener('keydown', this.handleKeyPress)

        // Chrome & Edge desktop have no problem with the basic YouTube Embed with ?autoplay=1
        // However Safari desktop and most/all mobile browsers do not successfully track the user gesture of clicking through the creation/loading of the iframe,
        // so they don't autoplay automatically. Instead we must load an additional 2 sequential JS files (1KB + 165KB) (un-br) for the YT Player API
        // TODO: Try loading the the YT API in parallel with our iframe and then attaching/playing it. #82
        this.needsYTApi =
          this.hasAttribute('js-api') ||
          navigator.vendor.includes('Apple') ||
          navigator.userAgent.includes('Mobi')
      }

      /**
       * Add a <link rel={preload | preconnect} ...> to the head
       */
      static addPrefetch(kind, url, as) {
        const linkEl = document.createElement('link')
        linkEl.rel = kind
        linkEl.href = url
        if (as) {
          linkEl.as = as
        }
        document.head.append(linkEl)
      }

      /**
       * Begin pre-connecting to warm up the iframe load
       * Since the embed's network requests load within its iframe,
       *   preload/prefetch'ing them outside the iframe will only cause double-downloads.
       * So, the best we can do is warm up a few connections to origins that are in the critical path.
       *
       * Maybe `<link rel=preload as=document>` would work, but it's unsupported: http://crbug.com/593267
       * But TBH, I don't think it'll happen soon with Site Isolation and split caches adding serious complexity.
       */
      static warmConnections() {
        if (LiteYTEmbed.preconnected) return

        // The iframe document and most of its subresources come right off youtube.com
        LiteYTEmbed.addPrefetch('preconnect', 'https://www.youtube-nocookie.com')
        // The botguard script is fetched off from google.com
        LiteYTEmbed.addPrefetch('preconnect', 'https://www.google.com')

        // Not certain if these ad related domains are in the critical path. Could verify with domain-specific throttling.
        LiteYTEmbed.addPrefetch('preconnect', 'https://googleads.g.doubleclick.net')
        LiteYTEmbed.addPrefetch('preconnect', 'https://static.doubleclick.net')

        LiteYTEmbed.preconnected = true
      }

      fetchYTPlayerApi() {
        if (window.YT || (window.YT && window.YT.Player)) return

        /* global YT */
        this.ytApiPromise = new Promise((resolve, reject) => {
          const el = document.createElement('script')
          el.src = 'https://www.youtube.com/iframe_api'
          el.async = true
          el.onload = (_) => {
            YT.ready(resolve)
          }
          el.onerror = reject
          this.append(el)
        })
      }

      /** Return the YT Player API instance. (Public L-YT-E API) */
      async getYTPlayer() {
        if (!this.playerPromise) {
          await this.activate()
        }

        return this.playerPromise
      }

      async addYTPlayerIframe() {
        this.fetchYTPlayerApi()
        await this.ytApiPromise

        const videoPlaceholderEl = document.createElement('div')
        this.append(videoPlaceholderEl)

        const paramsObj = Object.fromEntries(this.getParams().entries())

        this.playerPromise = new Promise((resolve) => {
          const player = new YT.Player(videoPlaceholderEl, {
            width: '100%',
            videoId: this.videoId,
            playerVars: paramsObj,
            events: {
              onReady: (event) => {
                event.target.playVideo()
                resolve(player)
              },
            },
          })
        })
      }

      // Add the iframe within <noscript> for indexability discoverability. See https://github.com/paulirish/lite-youtube-embed/issues/105
      addNoscriptIframe() {
        const iframeEl = this.createBasicIframe()
        const noscriptEl = document.createElement('noscript')
        // Appending into noscript isn't equivalant for mysterious reasons: https://html.spec.whatwg.org/multipage/scripting.html#the-noscript-element
        noscriptEl.innerHTML = iframeEl.outerHTML
        this.append(noscriptEl)
      }

      getParams() {
        const params = new URLSearchParams(this.getAttribute('params') || [])
        params.append('autoplay', '1')
        params.append('playsinline', '1')
        params.append('color', 'white')
        params.append('rel', '0')
        return params
      }

      async activate() {
        if (this.classList.contains('lyt-activated')) return
        const playBtnEl = this.querySelector('.lty-playbtn')
        if (playBtnEl && playBtnEl.hasAttribute('href')) {
          playBtnEl.removeAttribute('href')
        }
        this.classList.add('lyt-activated')

        if (this.style.backgroundImage !== 'unset') {
          this.style.backgroundImage = 'unset'
        }

        if (this.needsYTApi) {
          return this.addYTPlayerIframe(this.getParams())
        }

        const iframeEl = this.createBasicIframe()
        this.append(iframeEl)

        // Set focus for a11y
        iframeEl.focus()
      }

      handleKeyPress(event) {
        if (event.code === 'Enter' || event.code === 'Space') {
          this.activate()
        }
      }

      createBasicIframe() {
        const iframeEl = document.createElement('iframe')
        iframeEl.width = 560
        iframeEl.height = 315
        // No encoding necessary as [title] is safe. https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html#:~:text=Safe%20HTML%20Attributes%20include
        iframeEl.title = this.playLabel
        iframeEl.allow = 'accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture'
        iframeEl.allowFullscreen = true
        // AFAIK, the encoding here isn't necessary for XSS, but we'll do it only because this is a URL
        // https://stackoverflow.com/q/64959723/89484
        iframeEl.src = !this.short
          ? `https://www.youtube-nocookie.com/embed/${encodeURIComponent(this.videoId)}?${this.getParams().toString()}`
          : `https://www.youtube.com/embed/${encodeURIComponent(this.videoId)}?${this.getParams().toString()}&${this.short}`
        return iframeEl
      }

      /**
       * In the spirit of the `lowsrc` attribute and progressive JPEGs, we'll upgrade the reliable
       * poster image to a higher resolution one, if it's available.
       * Interestingly this sddefault webp is often smaller in filesize, but we will still attempt it second
       * because getting _an_ image in front of the user if our first priority.
       *
       * See https://github.com/paulirish/lite-youtube-embed/blob/master/youtube-thumbnail-urls.md for more details
       */
      upgradePosterImage() {
        // Defer to reduce network contention.
        setTimeout(() => {
          const webpUrl = `https://i.ytimg.com/vi_webp/${this.videoId}/sddefault.webp`
          const img = new Image()
          img.fetchPriority = 'low' // low priority to reduce network contention
          img.referrerpolicy = 'origin' // Not 100% sure it's needed, but https://github.com/ampproject/amphtml/pull/3940
          img.src = webpUrl
          img.onload = (e) => {
            // A pretty ugly hack since onerror won't fire on YouTube image 404. This is (probably) due to
            // Youtube's style of returning data even with a 404 status. That data is a 120x90 placeholder image.
            // … per "annoying yt 404 behavior" in the .md
            const noAvailablePoster = e.target.naturalHeight === 90 && e.target.naturalWidth === 120
            if (noAvailablePoster) return

            this.style.backgroundImage = `url("${webpUrl}")`
          }
        }, 100)
      }
    }
    // Register custom element
    customElements.define('lite-youtube', LiteYTEmbed)
  </script>

  <style is:global>
    lite-youtube {
      background-color: #000;
      position: relative;
      display: block;
      contain: content;
      background-position: center center;
      background-size: cover;
      cursor: pointer;
      aspect-ratio: 16/9;
      width: 100%;
      height: auto;
      border: 2px solid var(--color-accent);
      transition: all 0.3s ease;
      box-shadow: 0px 0px 15px rgb(212, 255, 0, 0.1);
    }

    /* gradient */
    lite-youtube::before {
      content: attr(data-title);
      display: block;
      position: absolute;
      top: 0;
      /* Pixel-perfect port of YT's gradient PNG, using https://github.com/bluesmoon/pngtocss plus optimizations */
      background-image: linear-gradient(
        180deg,
        rgb(0 0 0 / 67%) 0%,
        rgb(0 0 0 / 54%) 14%,
        rgb(0 0 0 / 15%) 54%,
        rgb(0 0 0 / 5%) 72%,
        rgb(0 0 0 / 0%) 94%
      );
      height: 99px;
      width: 100%;
      font-family: 'YouTube Noto', Roboto, Arial, Helvetica, sans-serif;
      color: hsl(0deg 0% 93.33%);
      text-shadow: 0 0 2px rgba(0, 0, 0, 0.5);
      font-size: 18px;
      padding: 25px 20px;
      overflow: hidden;
      white-space: nowrap;
      text-overflow: ellipsis;
      box-sizing: border-box;
    }

    lite-youtube:hover::before {
      color: white;
    }

    lite-youtube::after {
      content: '';
      margin: auto;
      width: 100px;
      height: 100px;
      position: absolute;
      background: rgba(0, 0, 0, 0.8);
      filter: blur(50px);
      inset: 0;
      z-index: -1;
    }

    lite-youtube > iframe {
      width: 100%;
      height: 100%;
    }

    /* play button */
    lite-youtube > .lty-playbtn {
      display: block;
      /* Make the button element cover the whole area for a large hover/click target… */
      width: 100%;
      height: 100%;
      /* …but visually it's still the same size */
      background: no-repeat center/100px 100px;
      /* YT's actual play button svg */
      background-color: white;
      -webkit-mask: url('data:image/svg+xml;utf8,<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"></path><path d="M719.4 499.1l-296.1-215A15.9 15.9 0 0 0 398 297v430c0 13.1 14.8 20.5 25.3 12.9l296.1-215a15.9 15.9 0 0 0 0-25.8zm-257.6 134V390.9L628.5 512 461.8 633.1z"></path></svg>')
        no-repeat center/100px 100px;
      mask: url('data:image/svg+xml;utf8,<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"></path><path d="M719.4 499.1l-296.1-215A15.9 15.9 0 0 0 398 297v430c0 13.1 14.8 20.5 25.3 12.9l296.1-215a15.9 15.9 0 0 0 0-25.8zm-257.6 134V390.9L628.5 512 461.8 633.1z"></path></svg>')
        no-repeat center/100px 100px;
      position: absolute;
      cursor: pointer;
      z-index: 1;
      filter: grayscale(100%);
      transition:
        filter 0.1s cubic-bezier(0, 0, 0.2, 1),
        transform 0.3s ease;
      border: 0;
      z-index: 1;
    }

    lite-youtube:hover > .lty-playbtn,
    lite-youtube .lty-playbtn:focus {
      background-color: pink;
      filter: none;
      transform: scale(1.23);
    }

    @media (prefers-reduced-motion) {
      lite-youtube:hover > .lty-playbtn,
      lite-youtube .lty-playbtn:focus {
        transition: 0s;
        transform: scale(1);
      }
    }

    /* Post-click styles */
    lite-youtube.lyt-activated {
      cursor: unset;
    }
    lite-youtube.lyt-activated::before,
    lite-youtube.lyt-activated > .lty-playbtn {
      opacity: 0;
      pointer-events: none;
    }

    .lyt-visually-hidden {
      clip: rect(0 0 0 0);
      clip-path: inset(50%);
      height: 1px;
      overflow: hidden;
      position: absolute;
      white-space: nowrap;
      width: 1px;
    }
  </style>
</div>
